<?php
class TradeMarketPage extends MemberPage {
	private static $default_parent = 'TradingPage';
	
    private static $db = array();

    private static $has_one = array(
		'TradeSetting' => 'TradeSetting'
	);

	function getSettingsFields() {
        $fields = parent::getSettingsFields();
        $fields->addFieldToTab('Root.Settings', DropdownField::create('TradeSettingID', _t('TradeMarketPage.TRADE_SETTING', 'Trade Setting'))->setSource(TradeSetting::get()->filter('IsActive', 1)->map()));
        return $fields;
    }
}

class TradeMarketPage_Controller extends MemberPage_Controller {

    /**
     * An array of actions that can be accessed via a request. Each array element
     * should be an action name, and the
     * permissions or conditions required to allow the user to access it.
     *
     * <code>
     * array (
     *     'action', // anyone can access this action
     *     'action' => true, // same as above
     *     'action' => 'ADMIN', // you must have ADMIN permissions to access this
     * action
     *     'action' => '->checkAction' // you can only access this action if
     * $this->checkAction() returns true
     * );
     * </code>
     *
     * @var array
     */
    private static $allowed_actions = array (
        'Form',
        'MarketActivityForm',
        'SellForm',
        'BuyForm',
        'cancel',
        'chart_json'
    );
	
	function index(){
		Requirements::combine_files('highstock.js', array(
			'trading/thirdparty/highstock/highstock.js',
			'trading/thirdparty/highstock/highcharts-more.js',
			'trading/thirdparty/highstock/exporting.js'
		));
		Requirements::javascript('trading/javascript/TradeMarketPage.js');
		Requirements::css('trading/css/TradeMarketPage.css');
		return $this->render();
	}
	
	function TradeOrderForm() {
        $fields = FieldList::create();
		$actions = FieldList::create();
        $field_list = array(
            'Created' => _t('TradeMarketPage.DATE', 'Date'),
            'Type' => _t('TradeMarketPage.TYPE', 'Type'),
            'Price' => array('title' => _t('TradeMarketPage.UNIT_PRICE', 'Unit Price ({currency})', '', array('currency' => SiteCurrencyConfig::current_site_currency())), 'classes' => 'text-right text-green'),
            'Unit' => array('title' => _t('TradeMarketPage.UNIT_TITLE', 'Unit'), 'classes' => 'text-right'),
            'Remaining' => array('title' => _t('TradeMarketPage.REMAINING', 'Remaining'), 'classes' => 'text-right'),
            'Amount' => array('title' => _t('TradeMarketPage.AMOUNT', 'Amount ({currency})', '', array('currency' => SiteCurrencyConfig::current_site_currency())), 'classes' => 'text-right text-green'),
            'Action' => array('title' => '', 'classes' => 'text-center')
        );
        $casting_list = array(
            'Created' => 'Datetime->Nice',
            'Price' => 'TradeCurrency->Nice',
            'Unit' => 'Int->Formatted',
            'Remaining' => 'Int->Formatted',
            'Amount' => 'TradeCurrency->Nice'
        );
        
        return DataListSearchForm::create($this, 'TradeOrderForm', 'TradeOrder', array('MemberID' => $this->CurrentMember()->ID, 'Status' => 'Pending', 'TradeSettingID' => $this->TradeSettingID), $fields, $actions)->setDataFieldList($field_list)->setFieldCasting($casting_list);
    }

	function MarketActivityForm() {
        $fields = FieldList::create();
		$actions = FieldList::create();
        $field_list = array(
            'Created' => _t('TradeMarketPage.DATE', 'Date'),
            'Price' => array('title' => _t('TradeMarketPage.UNIT_PRICE', 'Unit Price ({currency})', '', array('currency' => SiteCurrencyConfig::current_site_currency())), 'classes' => 'text-right text-green'),
            'Unit' => array('title' => _t('TradeMarketPage.UNIT_TITLE', 'Unit'), 'classes' => 'text-right'),
            'Amount' => array('title' => _t('TradeMarketPage.AMOUNT', 'Amount ({currency})', '', array('currency' => SiteCurrencyConfig::current_site_currency())), 'classes' => 'text-right text-green')
        );
        $casting_list = array(
            'Created' => 'Datetime->Nice',
            'Price' => 'TradeCurrency->Nice',
            'Unit' => 'Int->Formatted',
            'Amount' => 'TradeCurrency->Nice'
        );
        
        return DataListSearchForm::create($this, 'MarketActivityForm', 'TradeHistory', array('Type' => 'Buy', 'TradeSettingID' => $this->TradeSettingID), $fields, $actions)->setDataFieldList($field_list)->setFieldCasting($casting_list)->setSourceLimit(20);
    }

	function BuyingTrade() {
        $list = new ArrayList();
		$query = new SQLQuery(array());
		$result = $query->setFrom('TradeOrder')->addWhere("TradeOrder.Type = 'Buy'")->addWhere("TradeOrder.Status = 'Pending'")->addWhere(sprintf("TradeOrder.TradeSettingID = '%s'", $this->TradeSettingID))->setOrderBy('TradeOrder.Price', 'DESC')->setLimit(10)->setGroupBy('TradeOrder.Price')->selectField('SUM(TradeOrder.Remaining)', 'Unit')->selectField('SUM(TradeOrder.Price * TradeOrder.Remaining)', 'Amount')->selectField('TradeOrder.Price', 'Price')->execute();

		foreach($result as $item){
			$list->push(ArrayData::create(
				array(
					'Price' => DBField::create_field('TradeCurrency', $item['Price']),
					'Unit' => DBField::create_field('Int', $item['Unit']),
					'Amount' => DBField::create_field('TradeCurrency', $item['Amount'])
				)
			));
		}
		
		return $list;
    }

	function SellingTrade() {
        $list = new ArrayList();
		$query = new SQLQuery(array());
		$result = $query->setFrom('TradeOrder')->addWhere("TradeOrder.Type = 'Sell'")->addWhere("TradeOrder.Status = 'Pending'")->addWhere(sprintf("TradeOrder.TradeSettingID = '%s'", $this->TradeSettingID))->setOrderBy('TradeOrder.Price', 'ASC')->setLimit(10)->setGroupBy('TradeOrder.Price')->selectField('SUM(TradeOrder.Remaining)', 'Unit')->selectField('SUM(TradeOrder.Price * TradeOrder.Remaining)', 'Amount')->selectField('TradeOrder.Price', 'Price')->execute();
		foreach($result as $item){
			$list->push(ArrayData::create(
				array(
					'Price' => DBField::create_field('TradeCurrency', $item['Price']),
					'Unit' => DBField::create_field('Int', $item['Unit']),
					'Amount' => DBField::create_field('TradeCurrency', $item['Amount'])
				)
			));
		}
		
		return $list;
    }

	function BuyForm(){
    	$trade_setting = ($this->TradeSetting()->IsActive && $this->TradeSetting()->IsStarted && !$this->TradeSetting()->IsClosed) ? $this->TradeSetting() : false;
		
		if(!$trade_setting){
			$fields = FieldList::create(
				LiteralField::create('Note', _t('TradeMarketPage.MARKET_BUY_CLOSED', 'Sorry, currently trade market is closed for buy'))
			);
			
			$actions = FieldList::create();
			
			return Form::create($this, 'BuyForm', $fields, $actions);
		}
		

		if($trade_setting->RestrictMembers()->filter('TradeSetting_RestrictMembers.BuyOrder', 1)->byID($this->CurrentMember()->ID)){
			$trade_setting = false;
		}
		else {
			foreach($trade_setting->RestrictMembers()->filter('TradeSetting_RestrictMembers.BuyOrder', 1)->filter('TradeSetting_RestrictMembers.IncludeTeam', 1) as $member){
				$obj = Sponsor::get()->find('MemberID', $member->ID);
				if($obj && Sponsor::get()->filter('NLeft:GreaterThanOrEqual', (int)$obj->NLeft)->filter('NRight:LessThanOrEqual', (int)$obj->NRight)->filter('MemberID', $this->CurrentMember()->ID)->count()){
					$trade_setting = false;
					break;
				}
			}
		}
		
		if(!$trade_setting){
			$fields = FieldList::create(
				LiteralField::create('Note', _t('TradeMarketPage.RESTRICT_MARKET_BUY', 'Sorry, currently you\'re restrict for buy trade order.'))
			);
			
			$actions = FieldList::create();
			
			return Form::create($this, 'BuyForm', $fields, $actions);
		}
		
        $fields = FieldList::create(
        	HtmlEditorField_Readonly::create('AvailableTradeCoin', _t('TradeMarketPage.TRADE_COIN_BALANCE', 'Trade-Coin Balance'), $this->CurrentMember()->obj('TradeCoinAccountBalance')->Nice()),
        	HtmlEditorField_Readonly::create('CurrentPrice', _t('TradeMarketPage.CURRENT_PRICE', 'Current Price'), $trade_setting->dbObject('Price')->Nice()),
        	HtmlEditorField_Readonly::create('CurrentValue', _t('TradeMarketPage.CURRENT_VALUE', 'Current Value'), sprintf('%s (%s %s)', $this->CurrentMember()->TradeAccount($trade_setting->ID)->obj('CurrentValue')->Nice(), $this->CurrentMember()->TradeAccount($trade_setting->ID)->obj('TradeDeposit')->Nice(), _t('TradeMarketPage.RESERVED', 'Reserved'))),
        	HtmlEditorField_Readonly::create('AvailableBuy', _t('TradeMarketPage.AVAILABLE_FOR_BUY', 'Available for Buy'), sprintf('%s (%s %s)', $trade_setting->obj('CurrentAmount')->Nice(), $trade_setting->obj('CurrentUnit')->Formatted(), _t('TradeMarketPage.UNIT', 'unit'))),
        	BuyUnitField::create('BuyUnit', 0, $trade_setting->Price, $trade_setting->BuyFeePercentage)->setForcePrice($trade_setting->ForceBuyingPrice)
        );
			
        $actions = FieldList::create(
            FormAction::create('doBuy', _t('TradeMarketPage.BUY', 'BUY'))
        );
        
        $validators = RequiredFields::create('BuyUnit');
        
        return Form::create($this, 'BuyForm', $fields, $actions, $validators);
    }
    
    function doBuy($data, $form){
        try {
        	DB::getConn()->transactionStart();
			$trade = TradeOrder::create();
			$form->saveInto($trade);
			$trade->Type = 'Buy';
			$trade->MemberID = $this->CurrentMember()->ID;
			$trade->TradeSettingID = $this->TradeSettingID;
			$trade->write();
			DB::getConn()->transactionEnd();
			$form->sessionMessage(_t('TradeMarketPage.SUCCESS_BUY', 'Buy trade order have been perform successfully'), 'success');
        }
        catch(ValidationException $e) {
        	DB::getConn()->transactionRollback();
            SS_Log::log(new Exception(print_r($e->getMessage(), true)), SS_Log::NOTICE);
            $form->sessionMessage($e->getResult()->message(), 'error');
        }
        return $this->redirectBack();
    }
    
    function SellForm(){
    	$trade_setting = ($this->TradeSetting()->IsActive && $this->TradeSetting()->IsStarted && !$this->TradeSetting()->IsClosed) ? $this->TradeSetting() : false;
		
		if(!$trade_setting){
			$fields = FieldList::create(
				LiteralField::create('Note', _t('TradeMarketPage.MARKET_SELL_CLOSED', 'Sorry, currently trade market is closed for sell.'))
			);
			
			$actions = FieldList::create();
			
			return Form::create($this, 'SellForm', $fields, $actions);
		}
		
		if($trade_setting->RestrictMembers()->filter('TradeSetting_RestrictMembers.SellOrder', 1)->byID($this->CurrentMember()->ID)){
			$trade_setting = false;
		}
		else {
			foreach($trade_setting->RestrictMembers()->filter('TradeSetting_RestrictMembers.SellOrder', 1)->filter('TradeSetting_RestrictMembers.IncludeTeam', 1) as $member){
				$obj = Sponsor::get()->find('MemberID', $member->ID);
				if($obj && Sponsor::get()->filter('NLeft:GreaterThanOrEqual', (int)$obj->NLeft)->filter('NRight:LessThanOrEqual', (int)$obj->NRight)->filter('MemberID', $this->CurrentMember()->ID)->count()){
					$trade_setting = false;
					break;
				}
			}
		}
		
		if(!$trade_setting){
			$fields = FieldList::create(
				LiteralField::create('Note', _t('TradeMarketPage.RESTRICT_MARKET_SELL', 'Sorry, currently you\'re restrict for sell trade order.'))
			);
			
			$actions = FieldList::create();
			
			return Form::create($this, 'BuyForm', $fields, $actions);
		}
        $fields = FieldList::create(
        	HtmlEditorField_Readonly::create('AvailableUnit', _t('TradeMarketPage.AVAILABLE_UNIT', 'Available Unit'), sprintf('%s %s', $this->CurrentMember()->TradeAccount($trade_setting->ID)->obj('CurrentUnit')->Formatted(), _t('TradeMarketPage.UNIT', 'unit'))),
        	HtmlEditorField_Readonly::create('CurrentPrice', _t('TradeMarketPage.CURRENT_PRICE', 'Current Price'), $trade_setting->dbObject('Price')->Nice()),
        	HtmlEditorField_Readonly::create('CurrentValue', _t('TradeMarketPage.CURRENT_VALUE', 'Current Value'), sprintf('%s (%s %s)', $this->CurrentMember()->TradeAccount($trade_setting->ID)->obj('CurrentValue')->Nice(), $this->CurrentMember()->TradeAccount($trade_setting->ID)->obj('TradeDeposit')->Nice(), _t('TradeMarketPage.RESERVED', 'Reserved'))),
        	HtmlEditorField_Readonly::create('AvailableSell', _t('TradeMarketPage.AVAILABLE_FOR_SELL', 'Available for Sell'), sprintf('%s (%s %s)', $this->CurrentMember()->TradeAccount($trade_setting->ID)->obj('AvailableValue')->Nice(), $this->CurrentMember()->TradeAccount($trade_setting->ID)->obj('AvailableUnit')->Formatted(), _t('TradeMarketPage.UNIT', 'unit'))),
        	SellUnitField::create('SellUnit', 0, $trade_setting->Price, $trade_setting->SellFeePercentage)->setForcePrice($trade_setting->ForceSellingPrice)
        );
			
        $actions = FieldList::create(
            FormAction::create('doSell', _t('TradeMarketPage.SELL', 'SELL'))
        );
        
        $validators = RequiredFields::create('SellUnit');
        
        return Form::create($this, 'SellForm', $fields, $actions, $validators);
    }
    
    function doSell($data, $form){
        try {
        	DB::getConn()->transactionStart();
			$trade = TradeOrder::create();
			$form->saveInto($trade);
			$trade->Type = 'Sell';
			$trade->MemberID = $this->CurrentMember()->ID;
			$trade->TradeSettingID = $this->TradeSettingID;
			$trade->write();
			DB::getConn()->transactionEnd();
			$form->sessionMessage(_t('TradeMarketPage.SUCCESS_SELL', 'Sell trade order have been perform successfully'), 'success');
        }
        catch(ValidationException $e) {
        	DB::getConn()->transactionRollback();
            SS_Log::log(new Exception(print_r($e->getMessage(), true)), SS_Log::NOTICE);
            $form->sessionMessage($e->getResult()->message(), 'error');
        }
        return $this->redirectBack();
    }
	
	function cancel(){
		if($trade_order = TradeOrder::get()->filter('MemberID', $this->CurrentMember()->ID)->byID($this->request->param('ID'))){
			try {
	        	DB::getConn()->transactionStart();
				$trade_order->Status = 'Cancelled';
				$trade_order->write();
				DB::getConn()->transactionEnd();
				$this->setMessage('success', _t('TradeMarketPage.SUCCESS_CANCEL', 'Trade order have been cancelled'));
	        }
	        catch(ValidationException $e) {
	        	DB::getConn()->transactionRollback();
	            SS_Log::log(new Exception(print_r($e->getMessage(), true)), SS_Log::NOTICE);
				$this->setMessage('error', $e->getResult()->message());
	        }
			return $this->redirectBack();
		}
		
		return $this->httpError('404');
	}
	
	function chart_json(){
		$items = GroupedList::create(TradeHistory::get()->filter('Type', 'Buy')->filter('Created:GreaterThanOrEqual', date("Y-m-d 00:00:00", strtotime("-1 months")))->filter('TradeSettingID', $this->TradeSettingID)->sort('Created'));
		$price_data = array();
		$volume_data = array();
		foreach($items->groupBy('Created') as $group_items){
			$volume = 0;
			foreach($group_items as $item){
				$volume += $item->Unit;
			}
			$price_data[] = array(
				strtotime($group_items->first()->Created) * 1000,
				floatval($group_items->first()->Price)
			);
			
			$volume_data[] = array(
				strtotime($group_items->first()->Created) * 1000,
				$volume
			);
		}

		$price_data = array('name' => _t('TradeMarketPage.PRICE', 'Price'), 'type' => 'spline', 'yAxis' => 0, 'data' => $price_data);
		$volume_data = array('name' => _t('TradeMarketPage.TRADE_VOLUME', 'Trade Volume'), 'type' => 'column', 'yAxis' => 1, 'data' => $volume_data);
		return Convert::array2json(array($price_data, $volume_data));
	}
}